#include "stdafx.h"
#include "SceneSaver.h"

#include "tinyxml\tinyxml.h"

#include "Scene.h"
#include "Node.h"
#include "ModelNode.h"
#include "MultiObjectNode.h"
#include "Light.h"
#include "ParticleSystem.h"
#include "MeshInstance.h"
#include "Material.h"

using namespace mscene;

const char * boolToStr(bool value)
{
	return value ? "true" : "false";
}

static void saveNode(const Node & node, TiXmlElement * elem)
{
	TiXmlElement * nodeElem = new TiXmlElement("nodedata");
	elem->LinkEndChild(nodeElem);

	const uint BUFFER_SIZE = 16;
	char buffer[BUFFER_SIZE];

	TiXmlElement * posElem = new TiXmlElement("position");
	nodeElem->LinkEndChild(posElem);

	sprintf_s<BUFFER_SIZE>(buffer, "%.3f", node.Position.x);
	posElem->SetAttribute("x", buffer);

	sprintf_s<BUFFER_SIZE>(buffer, "%.3f", node.Position.y);
	posElem->SetAttribute("y", buffer);

	sprintf_s<BUFFER_SIZE>(buffer, "%.3f", node.Position.z);
	posElem->SetAttribute("z", buffer);


	TiXmlElement * orientElem = new TiXmlElement("orientation");
	nodeElem->LinkEndChild(orientElem);

	sprintf_s<BUFFER_SIZE>(buffer, "%.5f", node.Orientation.x);
	orientElem->SetAttribute("x", buffer);

	sprintf_s<BUFFER_SIZE>(buffer, "%.5f", node.Orientation.y);
	orientElem->SetAttribute("y", buffer);

	sprintf_s<BUFFER_SIZE>(buffer, "%.5f", node.Orientation.z);
	orientElem->SetAttribute("z", buffer);

	sprintf_s<BUFFER_SIZE>(buffer, "%.5f", node.Orientation.w);
	orientElem->SetAttribute("w", buffer);


	TiXmlElement * scaleElem = new TiXmlElement("scale");
	nodeElem->LinkEndChild(scaleElem);

	sprintf_s<BUFFER_SIZE>(buffer, "%.3f", node.Scale.x);
	scaleElem->SetAttribute("x", buffer);

	sprintf_s<BUFFER_SIZE>(buffer, "%.3f", node.Scale.y);
	scaleElem->SetAttribute("y", buffer);

	sprintf_s<BUFFER_SIZE>(buffer, "%.3f", node.Scale.z);
	scaleElem->SetAttribute("z", buffer);


	if(node.hasRigidBody())
	{
		TiXmlElement * physData = new TiXmlElement("physicsdata");
		elem->LinkEndChild(physData);

		TiXmlElement * rbElem = new TiXmlElement("rigidbody");
		physData->LinkEndChild(rbElem);
		rbElem->SetAttribute("static", boolToStr(node.IsStatic));
	}
}

static void saveModelNode(const ModelNode & node, TiXmlElement * parent)
{
	const char * name;
	if(node.PrimType == ModelNode::BOX) name = "box";
	else if(node.PrimType == ModelNode::SPHERE) name = "sphere";
	else if(node.PrimType == ModelNode::PLANE) name = "plane";	
	else return;
	TiXmlElement * elem = new TiXmlElement(name);
	parent->LinkEndChild(elem);

	saveNode(node, elem);

	TiXmlElement * materialData = new TiXmlElement("materialdata");
	elem->LinkEndChild(materialData);

	TiXmlElement * colorElem = new TiXmlElement("color");
	materialData->LinkEndChild(colorElem);

	const uint BUFFER_SIZE = 16;
	char buffer[BUFFER_SIZE];

	XMFLOAT3 color = XMCOLOR4F_TO_3F(node.getModel().Material_->DiffuseColor);

	sprintf_s<BUFFER_SIZE>(buffer, "%.3f", color.x);
	colorElem->SetAttribute("r", buffer);

	sprintf_s<BUFFER_SIZE>(buffer, "%.3f", color.y);
	colorElem->SetAttribute("g", buffer);

	sprintf_s<BUFFER_SIZE>(buffer, "%.3f", color.z);
	colorElem->SetAttribute("b", buffer);
}

static void savePointLight(const mgfx::PointLight & light, TiXmlElement * parent)
{
	TiXmlElement * elem = new TiXmlElement("pointlightdata");
	parent->LinkEndChild(elem);

	const uint BUFFER_SIZE = 16;
	char buffer[BUFFER_SIZE];

	TiXmlElement * valueElem = new TiXmlElement("radius");
	elem->LinkEndChild(valueElem);
	sprintf_s<BUFFER_SIZE>(buffer, "%.3f", light.Radius);
	valueElem->SetAttribute("value", buffer);

	valueElem = new TiXmlElement("falloff");
	elem->LinkEndChild(valueElem);
	sprintf_s<BUFFER_SIZE>(buffer, "%.3f", light.Falloff);
	valueElem->SetAttribute("value", buffer);

	valueElem = new TiXmlElement("intensity");
	elem->LinkEndChild(valueElem);
	sprintf_s<BUFFER_SIZE>(buffer, "%.3f", light.Intensity);
	valueElem->SetAttribute("value", buffer);

	valueElem = new TiXmlElement("color");
	elem->LinkEndChild(valueElem);
	sprintf_s<BUFFER_SIZE>(buffer, "%.3f", light.Color.x);
	valueElem->SetAttribute("r", buffer);
	sprintf_s<BUFFER_SIZE>(buffer, "%.3f", light.Color.y);
	valueElem->SetAttribute("g", buffer);
	sprintf_s<BUFFER_SIZE>(buffer, "%.3f", light.Color.z);
	valueElem->SetAttribute("b", buffer);
}

static void saveSpotLight(const mgfx::SpotLight & light, TiXmlElement * parent)
{
	TiXmlElement * elem = new TiXmlElement("spotlightdata");
	parent->LinkEndChild(elem);

	const uint BUFFER_SIZE = 16;
	char buffer[BUFFER_SIZE];

	TiXmlElement * valueElem = new TiXmlElement("radius");
	elem->LinkEndChild(valueElem);
	sprintf_s<BUFFER_SIZE>(buffer, "%.3f", light.Radius);
	valueElem->SetAttribute("value", buffer);

	valueElem = new TiXmlElement("falloff");
	elem->LinkEndChild(valueElem);
	sprintf_s<BUFFER_SIZE>(buffer, "%.3f", light.Falloff);
	valueElem->SetAttribute("value", buffer);

	valueElem = new TiXmlElement("intensity");
	elem->LinkEndChild(valueElem);
	sprintf_s<BUFFER_SIZE>(buffer, "%.3f", light.Intensity);
	valueElem->SetAttribute("value", buffer);

	valueElem = new TiXmlElement("color");
	elem->LinkEndChild(valueElem);
	sprintf_s<BUFFER_SIZE>(buffer, "%.3f", light.Color.x);
	valueElem->SetAttribute("r", buffer);
	sprintf_s<BUFFER_SIZE>(buffer, "%.3f", light.Color.y);
	valueElem->SetAttribute("g", buffer);
	sprintf_s<BUFFER_SIZE>(buffer, "%.3f", light.Color.z);
	valueElem->SetAttribute("b", buffer);

	valueElem = new TiXmlElement("spotangle");
	elem->LinkEndChild(valueElem);
	sprintf_s<BUFFER_SIZE>(buffer, "%.3f", light.SpotAngleRad);
	valueElem->SetAttribute("value", buffer);

	valueElem = new TiXmlElement("spotfalloff");
	elem->LinkEndChild(valueElem);
	sprintf_s<BUFFER_SIZE>(buffer, "%.3f", light.SpotFalloff);
	valueElem->SetAttribute("value", buffer);

	valueElem = new TiXmlElement("iscastingshadows");
	elem->LinkEndChild(valueElem);
	valueElem->SetAttribute("value", boolToStr(light.IsCastingShadows));
}

static void saveParticleSystem(const mgfx::ParticleSystem & ps, TiXmlElement * parent)
{
	TiXmlElement * elem = new TiXmlElement("particlesystem");
	parent->LinkEndChild(elem);

	TiXmlElement * valueElem = new TiXmlElement("testlol");
	elem->LinkEndChild(valueElem);
	valueElem->SetAttribute("value", boolToStr(true));
}

static void saveMultiObjectNode(const MultiObjectNode & node, TiXmlElement * parent)
{
	TiXmlElement * elem = new TiXmlElement("multinode");
	parent->LinkEndChild(elem);

	saveNode(node, elem);

	for(uint i = 0; i < node.GfxObjects.size(); i++)
	{
		const mgfx::GfxObject & gfxObj = *node.GfxObjects[i];

		if(gfxObj.Type == mgfx::GfxObject::POINT_LIGHT)
			savePointLight((const mgfx::PointLight &)gfxObj, elem);

		if(gfxObj.Type == mgfx::GfxObject::SPOT_LIGHT)
			saveSpotLight((const mgfx::SpotLight &)gfxObj, elem);

		if(gfxObj.Type == mgfx::GfxObject::PARTICLE_SYSTEM)
			saveParticleSystem((const mgfx::ParticleSystem &)gfxObj, elem);
	}
}

void SceneSaver::save(const std::string & file, const Scene & scene)
{
	TiXmlDocument doc;
	TiXmlElement* root = new TiXmlElement("nodes");
	doc.LinkEndChild(root);

	for(size_t i = 0; i < scene.mNodes.size(); i++)
	{
		const Node & node = *scene.mNodes[i];

		if(!node.Save)
			continue;

		if(node.Type == Node::MODEL)
			saveModelNode((const ModelNode &)node, root);

		if(node.Type == Node::MULTIOBJECT)
			saveMultiObjectNode((const MultiObjectNode &)node, root);
	}

	if(!doc.SaveFile(file.c_str()))
	{
		LOG_STREAM_ERROR("Failed to save scene file.");
	}

	doc.Clear();
}